Repository navigation
fix(onboarding): retry job title eligibility check after a failed request - #1412
Closed
gabrielseco wants to merge 2 commits into
Conversation
…uest check() recorded the new params before the request finished and only fired the request when params changed. With retry: false and staleTime: Infinity, a failed first attempt was never retried on later blurs unless a role field was edited. Always call queryClient.query when params are complete: a successful result is served from cache (no request), an errored/missing result refetches, and in-flight requests are deduped by the query cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
📦 Bundle Size Report
Size Limits
Largest Files (Top 5)
View All Files (75 total)
✅ Bundle size check passed |
Contributor
|
Deploy preview for remote-flows ready!
Deployed with vercel-action |
Contributor
|
Deploy preview for adp-cost-calculator ready!
Deployed with vercel-action |
Contributor
📊 Coverage Report✅ Coverage increased! 🎉
Detailed BreakdownLines Coverage
Statements Coverage
Functions Coverage
Branches Coverage
✅ Coverage check passed |
gabrielseco
added a commit
that referenced
this pull request
Sep 28, 2026
…r a failure A failed check must be retried on the next blur even when the role answers are unchanged. Depends on the fix in #1412; fails until it lands on the base branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gabrielseco
added a commit
that referenced
this pull request
Sep 28, 2026
…uest Same change as #1412, so this PR's retry test passes on its own. The hunk is identical, so merging both is clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gabrielseco
added a commit
that referenced
this pull request
Oct 8, 2026
* test(onboarding): cover the job title eligibility blur dedup Adds two integration tests against the rendered OnboardingFlow (not the headless hook) since that's the surface most consumers actually hit: blurring the role fields once they're complete fires the check, and a later blur with unchanged values doesn't fire it again. Also adds unit tests for the pure getJobTitleEligibilityParams (slug field gate, incomplete/invalid fields, a hidden param field still allowing the others through, job_title inclusion). Adds a minimal, purpose-built contract_details fixture (following the v1-italy-apl.ts "trimmed down" convention) with just the four fields the check cares about, rather than driving the full multi-hundred-field country schemas through this scenario. The real dedup bug this uncovered (paramsChanged never actually true) is fixed upstream in the parent branch, not in this commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(onboarding): give the dedup test time for a broken fetch to land Bugbot flagged that the second test's final assertion ran right after user.tab() resolves, but checkJobTitleEligibility is fire-and-forget from OnboardingForm's onBlur - it awaits handleValidation, parseFormValues, and the request itself, none of which user.tab() waits on. A regression that fires a second request wouldn't have reached the spy by assertion time, so the test would have passed even with a broken dedup. Verified by temporarily disabling both dedup layers (forcing paramsChanged true and setting staleTime: 0): without this fix the test still passed; with it, it correctly fails and catches the regression within the 200ms window. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(onboarding): explain the inverted waitFor in the dedup test The waitFor + .catch pattern for asserting an async side effect does not eventually happen is unusual enough to warrant a why-comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(onboarding): reword the waitFor comment in plain language Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(onboarding): cover retrying the job title eligibility check after a failure A failed check must be retried on the next blur even when the role answers are unchanged. Depends on the fix in #1412; fails until it lands on the base branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(onboarding): retry job title eligibility check after a failed request Same change as #1412, so this PR's retry test passes on its own. The hunk is identical, so merging both is clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(example): rename vite config to .mts to silence native config loader warning (#1394) The example package is "type": "commonjs" (dev_server.js and api/ use require), so Vite flagged ESM syntax in vite.config.ts as unsupported by the upcoming native config loader. The .mts extension marks just the config as ESM without flipping the whole package. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(scripts): send checkbox const value, not [true], in seed-onboarding (#1389) fakeValueFor() hardcoded [true] for checkbox fields, mimicking a jsonType-array shape that doesn't match how the real form submits const-based checkboxes (e.g. FRA's ack_non_eligible_job_titles, a jsonType string with const: "acknowledged"). parseFormValuesToAPI swaps the RHF boolean for field.const at submit time; mirror that here instead of sending the raw fill value, or the API 422s with a type mismatch. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * feat(scripts): seed onboarding directly against a gateway env (#1371) * feat(scripts): seed onboarding directly against a gateway env Lets seed-onboarding.mjs bypass the local example dev server via --env=<name>, reusing example/api/{utils,get_token,proxy}.js for auth against .env.<env> at the repo root. Prints a ready-to-click app link (?employmentId= prefilled, via VITE_APP_URL) when set. Onboarding.tsx now reads employmentId from the URL query string so that link actually prefills the intro form. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(skills): add verify-sandbox-deployed-app skill Captures the manual sandbox-verification loop (seed via --env=sandbox, log past the Vercel password gate with VITE_APP_PASSWORD, drive the flow, check the claim) as a repeatable skill instead of re-deriving it each time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * style: fix oxfmt formatting in verify-sandbox-deployed-app skill Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(skills): remove verify-sandbox-deployed-app skill Split into its own PR (#1379) so it can be reviewed independently of the seed-onboarding env flag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(e2e): give sandbox onboarding specs room for slow backend writes The basic and Germany onboarding specs create real employments against the sandbox and ran against Playwright's 30s test / 5s expect defaults. Traces from PR #1371 and main show nothing hanging: Germany reached the final ILA sign click at 27.2s, leaving <3s for the sign POST, and Spain failed step-title assertions while ~3s employment writes were in flight. - test.slow() on both onboarding specs (90s budget) - expectOnboardingStep helper with a 15s timeout for step transitions, which are gated on sandbox writes rather than rendering - wait on the ILA sign POST and assert it succeeded before checking the dialog closed, so a backend rejection reports as such Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e): gate onboarding steps on save responses, set timeouts globally Replaces the per-spec test.slow() and the 15s step-title timeout with: - submitAndWaitForSave / clickAndWaitForSave helpers that wait for the request each step's submit fires (POST /v1/employments, POST engagement-agreement-details, PATCH /v1/employments/{id}, PUT benefit-offers, POST .../sign) and assert it succeeded, instead of waiting for the "Loading..." text to disappear. A backend failure now reports method, path and status. - Global timeout (60s) and expect timeout (10s) in playwright.config.ts, matching Dragon's defaults. Response waits make failures precise but do not make the sandbox faster; the Germany flow alone takes ~30s on a good day, so the 30s default budget has to go up regardless. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * 1.57.0 (#1407) * chore: prepare release v1.57.0 * docs: group 1.57.0 changelog by type Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * update changelog --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * docs: surface the inline-comment convention in CLAUDE.md (#1405) * docs: surface the inline-comment convention in CLAUDE.md The comment-density rule (zero by default, one line max for a genuinely non-obvious why) only lived in the untracked CLAUDE.local.md and in .cursor/BUGBOT.md, which Cursor's Bugbot reads for its own reviews but Claude Code doesn't load as project instructions. Add a pointer in CLAUDE.md's Conventions section so it's visible up front instead of surfacing after the fact via a bot comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: describe what a good inline comment looks like Extends the opt-in comment rule with what "good" actually means when a comment does get written (the rare non-obvious-why case, or on explicit request): plain English a newcomer can follow, not jargon-dense shorthand - even if that takes a couple of sentences. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: exempt human-requested comments from the one-line cap The one-line cap exists to stop unprompted, jargon-dense comments - it's not meant to force a human-requested explanation into an unreadable single line. Carves out that exception in both CLAUDE.md and .cursor/BUGBOT.md: a comment explicitly asked for can take as many plain sentences as it needs; one added on the AI's own initiative for the non-obvious-why case stays capped at one line, and shouldn't exist at all if the code it's next to is actually simple. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * docs: document import order convention (#1404) React first, then external packages, then @remoteoss/* packages, then absolute (@/) imports, then relative imports. Not enforced by oxlint or oxfmt today (verified: import/order is not a supported oxlint rule and oxfmt does not reorder imports) — this documents the existing manual convention so it's explicit for humans and Claude Code. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore(deps): update dependency @remoteoss/remote-json-schema-form-kit to v1.1.2 (#1409) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(onboarding): block invite while employment is in job_title_review (#1403) Dragon hard-gates its Invite button on employment.status !== 'job_title_review' (SendInvitation.jsx); we didn't. Tiger already rejects the invite call server-side while in this status (409 'Can't invite employee in status job_title_review' on the partner API, 422 on the internal one), so this closes the client-side gap rather than introducing new blocking behavior. job_title_review is independent of the (still unmerged) job title eligibility check feature flag: it's part of the generated Employment['status'] type already, and Tiger can put an employment into this status any time its stage-2 AI check needs human review, so the SDK should respect it regardless of whether a partner has opted into that feature. Addresses checklist item 1 in JOB_TITLE_ELIGIBILITY_CHECKLIST.md (on feat/onboarding-job-title-eligibility-check). Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore: ignore CLAUDE.local.md (#1400) * chore: ignore CLAUDE.local.md Adds an explicit .gitignore entry for CLAUDE.local.md, a local-only companion to CLAUDE.md for per-developer notes that shouldn't be checked in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: copy CLAUDE.local.md into new worktrees CLAUDE.local.md is gitignored, so a plain `git worktree add` doesn't carry it over and Claude Code loses that context in worktree sessions. Copy it the same way example/.env is already seeded. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(scripts): copy root .env.<env> files into new worktrees npm run seed:onboarding -- --env=<name> reads .env.<name> from the repo root, but create-worktree.ts only ever copied example/.env - a fresh worktree silently lost access to gateway-targeting credentials like .env.sandbox even though it's a root-level, not example/-level, file. Mirrors the CLAUDE.local.md copy step just above it: glob root for any .env.* file and copy each one into the new worktree. Also gitignores .env.* (previously only .env itself was ignored), since these files carry real credentials and weren't excluded from `git add -A`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore(deps-dev): update dependency dotenv to v18.0.1 (#1410) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps-dev): update dependency tsx to v4.23.15 (#1411) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
gabrielseco
added a commit
that referenced
this pull request
Oct 8, 2026
…1406) * feat(onboarding): add job_title_eligibility feature flag Splits the flag declaration out of #1395 so it can land on its own: adds 'job_title_eligibility' to the OnboardingFeatures union (with its JSDoc) and enables it in the example app. No behavior wired up yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * feat(onboarding): wire onBlur events to job title eligibility check Adds the useJobTitleEligibilityCheck hook and jobTitleEligibilityCheckOptions query, and wires OnboardingForm's onBlur to call the flowBag's new checkJobTitleEligibility(values). The check only runs on the contract_details step, when 'job_title_eligibility' is enabled, and when the role fields (role_description, role_is_onsite, role_requires_license) are filled and valid. Dedup: params are compared against the previous check (fast-deep-equal) before firing a request, and jobTitleEligibilityCheckOptions sets staleTime: Infinity since the query key already encodes employmentId + params. Without both, react-query's built-in dedup only covers truly concurrent calls with an identical key — an imperative queryClient.query() call is not a mounted observer, so it treats every call as a fresh mount and refetches under the default staleTime: 0. The result isn't consumed yet (no UI reacts to eligibility yet) - follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(onboarding): drop debug logs and fix job title eligibility dedup Removes the console.log statements left over from development. Also fixes a real bug surfaced while writing tests for this: paramsChanged was read immediately after calling setParams(updaterFn), assuming the functional updater runs synchronously. It doesn't reliably do that, so paramsChanged came back false almost every time - meaning the eligibility check never actually fired a request, for any input. Fixed by comparing against the params value already in the closure before calling setParams, instead of depending on a side effect inside the updater. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: remove accidentally committed local settings file .claude/settings.json is a personal/local permission-allowlist file, not meant for this PR - it got swept into the previous commit because it was already staged from earlier local work. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(onboarding): match checkJobTitleEligibility's docs to its behavior The JSDoc claimed the check also runs on entering the contract details step and before submitting it, but nothing calls it that way - only OnboardingForm's onBlur does. Since useOnboarding is reachable through the public ./flows/* entry point, this overstated the contract for anyone building a custom UI around the headless hook. Reworded to describe only what's actually wired up, and pointed custom UIs at calling checkJobTitleEligibility themselves for other trigger points instead of implying it happens automatically. Also drops stepValues, initialContractDetailsValues, and fieldValues from useJobTitleEligibilityCheck's params - they were threaded through from hooks.tsx but never used inside the hook, leftover scaffolding for the same unimplemented triggers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(onboarding): close race between overlapping eligibility checks check() compared nextParams against params captured from the render closure after two awaits (handleValidation, parseFormValues). If a second blur's check() started before the first one's setParams had committed a re-render, both calls would compare against the same stale params - depending on timing, that can either fire a duplicate request or wrongly treat a later complete result as unchanged and skip it. Flagged by Bugbot on PR #1406, independently of the same race I'd already called out in review. Fixed by tracking params in a ref alongside the state (state still drives the reactive useQuery's enabled/key). Ref writes are synchronous and immediately visible to any concurrently-resolving check() call, so the comparison always sees the latest known params regardless of render timing, closing the window entirely rather than narrowing it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * revert * test(onboarding): cover the job title eligibility blur dedup (#1408) * test(onboarding): cover the job title eligibility blur dedup Adds two integration tests against the rendered OnboardingFlow (not the headless hook) since that's the surface most consumers actually hit: blurring the role fields once they're complete fires the check, and a later blur with unchanged values doesn't fire it again. Also adds unit tests for the pure getJobTitleEligibilityParams (slug field gate, incomplete/invalid fields, a hidden param field still allowing the others through, job_title inclusion). Adds a minimal, purpose-built contract_details fixture (following the v1-italy-apl.ts "trimmed down" convention) with just the four fields the check cares about, rather than driving the full multi-hundred-field country schemas through this scenario. The real dedup bug this uncovered (paramsChanged never actually true) is fixed upstream in the parent branch, not in this commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(onboarding): give the dedup test time for a broken fetch to land Bugbot flagged that the second test's final assertion ran right after user.tab() resolves, but checkJobTitleEligibility is fire-and-forget from OnboardingForm's onBlur - it awaits handleValidation, parseFormValues, and the request itself, none of which user.tab() waits on. A regression that fires a second request wouldn't have reached the spy by assertion time, so the test would have passed even with a broken dedup. Verified by temporarily disabling both dedup layers (forcing paramsChanged true and setting staleTime: 0): without this fix the test still passed; with it, it correctly fails and catches the regression within the 200ms window. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(onboarding): explain the inverted waitFor in the dedup test The waitFor + .catch pattern for asserting an async side effect does not eventually happen is unusual enough to warrant a why-comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(onboarding): reword the waitFor comment in plain language Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(onboarding): cover retrying the job title eligibility check after a failure A failed check must be retried on the next blur even when the role answers are unchanged. Depends on the fix in #1412; fails until it lands on the base branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(onboarding): retry job title eligibility check after a failed request Same change as #1412, so this PR's retry test passes on its own. The hunk is identical, so merging both is clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(example): rename vite config to .mts to silence native config loader warning (#1394) The example package is "type": "commonjs" (dev_server.js and api/ use require), so Vite flagged ESM syntax in vite.config.ts as unsupported by the upcoming native config loader. The .mts extension marks just the config as ESM without flipping the whole package. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(scripts): send checkbox const value, not [true], in seed-onboarding (#1389) fakeValueFor() hardcoded [true] for checkbox fields, mimicking a jsonType-array shape that doesn't match how the real form submits const-based checkboxes (e.g. FRA's ack_non_eligible_job_titles, a jsonType string with const: "acknowledged"). parseFormValuesToAPI swaps the RHF boolean for field.const at submit time; mirror that here instead of sending the raw fill value, or the API 422s with a type mismatch. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * feat(scripts): seed onboarding directly against a gateway env (#1371) * feat(scripts): seed onboarding directly against a gateway env Lets seed-onboarding.mjs bypass the local example dev server via --env=<name>, reusing example/api/{utils,get_token,proxy}.js for auth against .env.<env> at the repo root. Prints a ready-to-click app link (?employmentId= prefilled, via VITE_APP_URL) when set. Onboarding.tsx now reads employmentId from the URL query string so that link actually prefills the intro form. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(skills): add verify-sandbox-deployed-app skill Captures the manual sandbox-verification loop (seed via --env=sandbox, log past the Vercel password gate with VITE_APP_PASSWORD, drive the flow, check the claim) as a repeatable skill instead of re-deriving it each time. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * style: fix oxfmt formatting in verify-sandbox-deployed-app skill Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(skills): remove verify-sandbox-deployed-app skill Split into its own PR (#1379) so it can be reviewed independently of the seed-onboarding env flag. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(e2e): give sandbox onboarding specs room for slow backend writes The basic and Germany onboarding specs create real employments against the sandbox and ran against Playwright's 30s test / 5s expect defaults. Traces from PR #1371 and main show nothing hanging: Germany reached the final ILA sign click at 27.2s, leaving <3s for the sign POST, and Spain failed step-title assertions while ~3s employment writes were in flight. - test.slow() on both onboarding specs (90s budget) - expectOnboardingStep helper with a 15s timeout for step transitions, which are gated on sandbox writes rather than rendering - wait on the ILA sign POST and assert it succeeded before checking the dialog closed, so a backend rejection reports as such Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(e2e): gate onboarding steps on save responses, set timeouts globally Replaces the per-spec test.slow() and the 15s step-title timeout with: - submitAndWaitForSave / clickAndWaitForSave helpers that wait for the request each step's submit fires (POST /v1/employments, POST engagement-agreement-details, PATCH /v1/employments/{id}, PUT benefit-offers, POST .../sign) and assert it succeeded, instead of waiting for the "Loading..." text to disappear. A backend failure now reports method, path and status. - Global timeout (60s) and expect timeout (10s) in playwright.config.ts, matching Dragon's defaults. Response waits make failures precise but do not make the sandbox faster; the Germany flow alone takes ~30s on a good day, so the 30s default budget has to go up regardless. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * 1.57.0 (#1407) * chore: prepare release v1.57.0 * docs: group 1.57.0 changelog by type Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * update changelog --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * docs: surface the inline-comment convention in CLAUDE.md (#1405) * docs: surface the inline-comment convention in CLAUDE.md The comment-density rule (zero by default, one line max for a genuinely non-obvious why) only lived in the untracked CLAUDE.local.md and in .cursor/BUGBOT.md, which Cursor's Bugbot reads for its own reviews but Claude Code doesn't load as project instructions. Add a pointer in CLAUDE.md's Conventions section so it's visible up front instead of surfacing after the fact via a bot comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: describe what a good inline comment looks like Extends the opt-in comment rule with what "good" actually means when a comment does get written (the rare non-obvious-why case, or on explicit request): plain English a newcomer can follow, not jargon-dense shorthand - even if that takes a couple of sentences. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs: exempt human-requested comments from the one-line cap The one-line cap exists to stop unprompted, jargon-dense comments - it's not meant to force a human-requested explanation into an unreadable single line. Carves out that exception in both CLAUDE.md and .cursor/BUGBOT.md: a comment explicitly asked for can take as many plain sentences as it needs; one added on the AI's own initiative for the non-obvious-why case stays capped at one line, and shouldn't exist at all if the code it's next to is actually simple. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * docs: document import order convention (#1404) React first, then external packages, then @remoteoss/* packages, then absolute (@/) imports, then relative imports. Not enforced by oxlint or oxfmt today (verified: import/order is not a supported oxlint rule and oxfmt does not reorder imports) — this documents the existing manual convention so it's explicit for humans and Claude Code. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore(deps): update dependency @remoteoss/remote-json-schema-form-kit to v1.1.2 (#1409) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix(onboarding): block invite while employment is in job_title_review (#1403) Dragon hard-gates its Invite button on employment.status !== 'job_title_review' (SendInvitation.jsx); we didn't. Tiger already rejects the invite call server-side while in this status (409 'Can't invite employee in status job_title_review' on the partner API, 422 on the internal one), so this closes the client-side gap rather than introducing new blocking behavior. job_title_review is independent of the (still unmerged) job title eligibility check feature flag: it's part of the generated Employment['status'] type already, and Tiger can put an employment into this status any time its stage-2 AI check needs human review, so the SDK should respect it regardless of whether a partner has opted into that feature. Addresses checklist item 1 in JOB_TITLE_ELIGIBILITY_CHECKLIST.md (on feat/onboarding-job-title-eligibility-check). Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore: ignore CLAUDE.local.md (#1400) * chore: ignore CLAUDE.local.md Adds an explicit .gitignore entry for CLAUDE.local.md, a local-only companion to CLAUDE.md for per-developer notes that shouldn't be checked in. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * chore: copy CLAUDE.local.md into new worktrees CLAUDE.local.md is gitignored, so a plain `git worktree add` doesn't carry it over and Claude Code loses that context in worktree sessions. Copy it the same way example/.env is already seeded. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(scripts): copy root .env.<env> files into new worktrees npm run seed:onboarding -- --env=<name> reads .env.<name> from the repo root, but create-worktree.ts only ever copied example/.env - a fresh worktree silently lost access to gateway-targeting credentials like .env.sandbox even though it's a root-level, not example/-level, file. Mirrors the CLAUDE.local.md copy step just above it: glob root for any .env.* file and copy each one into the new worktree. Also gitignores .env.* (previously only .env itself was ignored), since these files carry real credentials and weren't excluded from `git add -A`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> * chore(deps-dev): update dependency dotenv to v18.0.1 (#1410) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore(deps-dev): update dependency tsx to v4.23.15 (#1411) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
If the job title eligibility check fails once (e.g. a network or server error), it now runs again the next time the user leaves a field. Before, it was never retried unless the user changed their role answers.
Why
Cursor Bugbot flagged this on #1406 ("Failed checks skip later retries"). A single transient failure left the employment without a recorded check, and the user couldn't recover it without editing a role field.
What changed
Toggle details
useJobTitleEligibilityCheck.check()no longer gates the request on "params changed". The query cache already handles dedup:jobTitleEligibilityCheckOptionsusesstaleTime: Infinity, so a successful result is returned from cache with no request, while an errored (no data) query is stale and refetches. In-flight requests are deduped byquery.fetch.setParamsstill only runs when params change, so theuseQuerykey is stable.main. No public API change.Screenshots
N/A
Related Resources
Testing
example/app in a browserjob_title_eligibility🤖 Generated with Claude Code